//////////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include "telnetdef.h"
#include "rtelnetsocket.h"

CRTelnetSocket::CRTelnetSocket(int nTimeOut):CRTSocket(nTimeOut)
{
}

CRTelnetSocket::~CRTelnetSocket(void)
{
}

void CRTelnetSocket::Purge()
{
	char buf[1024];

	//must be while here
	while ( WaitForRead(25) )
	{
		if ( Read(buf, sizeof(buf))<=0 )
			break;
	}
}

int	CRTelnetSocket::Read( char* pData, size_t stSize)
{
	int nRes=0;
	int nOffset=0;
	int nMaxLoops(3);
		
	do
	{
		nRes = CRTSocket::Read( pData, stSize );
	
		if (nRes>=3)
		{
			nMaxLoops++;
			nOffset = ProcessTelnetCMD((unsigned char*)pData, stSize);
	
			if ( nOffset )
			{
				if ( nRes==nOffset )
				{
					memset(pData, 0, nRes);
				}
				else
				{
					char* pPos = pData;
					char* pStart = pData+nOffset;
					char* pEnd   = pData+nRes;
	
					if (*pStart==CR) pStart++;
					if (*pStart==LF) pStart++;
	
					while (pStart!=pEnd)
					{
						*pPos=*pStart;
						pPos++;
						pStart++;
					}
				}
	
				nRes-=nOffset;
			}
		}
		
	}while(nOffset && !nRes && nMaxLoops-->0);

	return nRes;
}

int	CRTelnetSocket::ProcessTelnetCMD( unsigned char* pData, size_t stSize )
{
	int nRes(0);

	for (size_t i=0;i<stSize;i+=3)
	{
		//telnet cmd
		if ( IAC == pData[i] )
		{
			nRes+=3;

			switch( pData[i+1] )
			{
				case DO:
				{
					ProcessTelnetDOCMD(pData[i+2]);
				}
				break;

				case WILL:
				{
					ProcessTelnetWILLCMD(pData[i+2]);
				}
				break;
				
				default:
				{
					ProcessTelnetNotSupportedCMD(pData[i+2]);
				}
				break;
			}
		}
		else
		{
			break;
		}
	}

	return nRes;
}

void CRTelnetSocket::InitConversation()
{
	unsigned char buf[3];
	buf[0]=IAC;

	//will echo
	buf[1]=WILL;
	buf[2]=TELOPT_ECHO;
	Write((const char*)buf, sizeof(buf));

	//wont strdup
	buf[1]=WONT;
	buf[2]=TELOPT_SUPDUP;
	Write((const char*)buf, sizeof(buf));

	//DO	echo
	buf[1]=DO;
	buf[2]=TELOPT_ECHO;
	Write((const char*)buf, sizeof(buf));

	//DO	SGA
	buf[1]=DO;
	buf[2]=TELOPT_SGA;
	Write((const char*)buf, sizeof(buf));
}


//DO:253/0xFD
void CRTelnetSocket::ProcessTelnetDOCMD(unsigned char val)
{
	unsigned char buf[3];
	buf[0]=IAC;
	buf[1]=DO;
	buf[2]=IAC;

	switch(val)
	{
		case TELOPT_ECHO:
		{
			buf[2]=TELOPT_ECHO;
		}
		break;

		case TELOPT_SGA:
		{
			buf[2]=TELOPT_SGA;
		}
		break;

		case TELOPT_LFLOW:
		{
			buf[1]=WONT;
			buf[2]=TELOPT_LFLOW;
		}
		break;
		
		//Linux term support
		default:
		{
			buf[1]=WONT;
			buf[2]=val;
		}
		break;

		/*
		default:
		{
			fprintf(stderr, "Unknown DO cmd: %d\n", val);
		}
		break;
		*/
	}

	if ( IAC!=buf[2] )
	{
		Write( (const char*) buf, sizeof(buf) );
	}
}

//WILL:251/0xFB
void CRTelnetSocket::ProcessTelnetWILLCMD(unsigned char val)
{
	unsigned char buf[3];
	buf[0]=IAC;
	buf[1]=DO;
	buf[2]=IAC;

	switch(val)
	{
		case TELOPT_ECHO:
		{
			buf[2]=TELOPT_ECHO;
		}
		break;

		case TELOPT_SGA:
		{
			buf[2]=TELOPT_SGA;
		}
		break;

		default:
		{
			buf[1]=WONT;
			buf[2]=val;
			//fprintf(stderr, "Unknown Will cmd: %x\n", val);
		}
		break;
	}

	if ( IAC!=buf[2] )
	{
		Write( (const char*)buf, sizeof(buf) );
	}
}

void CRTelnetSocket::ProcessTelnetNotSupportedCMD(unsigned char val)
{
}
